﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DynamiteXml;
using FileStorage.Enums.Behaviours;
using FileStorage.Handler;
using FileStorage.MetaData;

namespace FileStorage.Caching
{
    public static class FileStorageCacherFacade
    {
        private static object locker = new object();

        private static HashSet<ICacheContainer> registeredCacheContainers = new HashSet<ICacheContainer>();

        public static byte[] GetBytesData(string fileStorageName, Guid dataIdentifier, ICacheContainer cacheContainer)
        {
            byte[] result;

            CacheMethodInvocationsEnum cacheMethodInvocationsEnum = CacheMethodInvocationsEnum.GetBytesData;

            lock(locker)
            {
                cacheContainer.Validate(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum);
                if (cacheContainer.Exists(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum))
                {
                    result = cacheContainer.Get(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum) as byte[];
                }
                else
                {
                    var fileStorageHandler = FileStorageHandler.Open(fileStorageName);
                    result = fileStorageHandler.GetBytesData(dataIdentifier, StreamStateBehaviour.OpenNewStreamForReading, StreamStateBehaviour.OpenNewStreamForReading);
                    cacheContainer.Store(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum, result);
                }
            }

            return result;
        }

        public static IDynamiteXml GetObjectData(string fileStorageName, Guid dataIdentifier, ICacheContainer cacheContainer)
        {
            IDynamiteXml result;

            CacheMethodInvocationsEnum cacheMethodInvocationsEnum = CacheMethodInvocationsEnum.GetObjectData;

            lock (locker)
            {
                cacheContainer.Validate(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum);

                if (cacheContainer.Exists(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum))
                {
                    result = cacheContainer.Get(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum) as IDynamiteXml;
                }
                else
                {
                    var fileStorageHandler = FileStorageHandler.Open(fileStorageName);
                    result = fileStorageHandler.GetObjectData(dataIdentifier, StreamStateBehaviour.OpenNewStreamForReading, StreamStateBehaviour.OpenNewStreamForReading);
                    cacheContainer.Store(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum, result);
                }
            }

            return result;
        }

        public static string GetStringData(string fileStorageName, Guid dataIdentifier, ICacheContainer cacheContainer)
        {
            string result;

            CacheMethodInvocationsEnum cacheMethodInvocationsEnum = CacheMethodInvocationsEnum.GetStringData;

            lock (locker)
            {
                cacheContainer.Validate(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum);

                if (cacheContainer.Exists(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum))
                {
                    result = cacheContainer.Get(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum) as string;
                }
                else
                {
                    var fileStorageHandler = FileStorageHandler.Open(fileStorageName);
                    result = fileStorageHandler.GetStringData(dataIdentifier, StreamStateBehaviour.OpenNewStreamForReading, StreamStateBehaviour.OpenNewStreamForReading);
                    cacheContainer.Store(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum, result);
                }
            }

            return result;
        }

        public static string GetStringData(string fileStorageName, Guid dataIdentifier, Encoding encoding, ICacheContainer cacheContainer)
        {
            string result;

            CacheMethodInvocationsEnum cacheMethodInvocationsEnum = CacheMethodInvocationsEnum.GetStringDataWithEncoding;

            lock (locker)
            {
                cacheContainer.Validate(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum);

                if (cacheContainer.Exists(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum))
                {
                    result = cacheContainer.Get(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum) as string;
                }
                else
                {
                    var fileStorageHandler = FileStorageHandler.Open(fileStorageName);
                    result = fileStorageHandler.GetStringData(dataIdentifier, encoding, StreamStateBehaviour.OpenNewStreamForReading, StreamStateBehaviour.OpenNewStreamForReading);
                    cacheContainer.Store(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum, result);
                }
            }

            return result;
        }

        public static MetaDataContainer GetMetaData(string fileStorageName, Guid dataIdentifier, ICacheContainer cacheContainer)
        {
            MetaDataContainer result;

            CacheMethodInvocationsEnum cacheMethodInvocationsEnum = CacheMethodInvocationsEnum.GetMetaData;

            lock (locker)
            {
                cacheContainer.Validate(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum);

                if (cacheContainer.Exists(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum))
                {
                    result = cacheContainer.Get(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum) as MetaDataContainer;
                }
                else
                {
                    
                    result = FileStorageFacade.GetMetaData(fileStorageName, dataIdentifier);
                    cacheContainer.Store(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum, result);
                }
            }

            return result;
        }

        /// <summary>
        /// Retrieves all data identifiers according to the cache
        /// </summary>
        /// <param name="fileStorageName"></param>
        /// <returns></returns>
        public static List<Guid> GetAllDataIdentifiersBasedUponFileStorageIndexFile(string fileStorageName, ICacheContainer cacheContainer)
        {
            List<Guid> result;

            CacheMethodInvocationsEnum cacheMethodInvocationsEnum = CacheMethodInvocationsEnum.GetDataIdentifiersViaIndexFile;

            Guid dataIdentifier = Guid.Empty;   // the index file is not a specific dataidentifier, thus we set it to empty

            lock (locker)
            {
                cacheContainer.Validate(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum);

                if (cacheContainer.Exists(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum))
                {
                    result = cacheContainer.Get(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum) as List<Guid>;
                }
                else
                {
                    result = FileStorageFacade.GetAllDataIdentifiersBasedUponFileStorageIndexFile(fileStorageName);
                    cacheContainer.Store(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum, result);
                }
            }

            return result;
        }

        public static void RegisterCacheContainer(ICacheContainer cacheContainer)
        {
            if (!registeredCacheContainers.Contains(cacheContainer))
            {
                registeredCacheContainers.Add(cacheContainer);
            }
        }

        /// <summary>
        /// Invalidates all items in the cache of all registered cache containers
        /// </summary>
        public static void InvalidateAll()
        {
            foreach (var cacheContainer in registeredCacheContainers)
            {
                cacheContainer.InvalidateAll();
            }
        }

        public enum CacheMethodInvocationsEnum : int
        {
            Invalid = 0,
            GetBytesData = 1,
            GetObjectData = 2,
            GetStringData = 3,
            GetStringDataWithEncoding = 4,
            GetDataIdentifiersViaIndexFile = 5,
            GetMetaData = 6
        }
    }
}
